﻿using System;
using System.Collections.Generic;
using Autodesk.Revit.DB;

namespace YArchitech.BIM.XRouter
{
	public class ShortestPath
	{
		public ShortestPath(UndirectGraph unGraph)
		{
			this.graph = unGraph;
		}

		public RouteResult RouteShortestPath(XYZ ptFrom, XYZ ptTo, out List<ElementId> lstEdge, out List<XYZ> lstVertex)
		{
			lstEdge = new List<ElementId>();
			lstVertex = new List<XYZ>();
			Vertex vertexByPoint = this.graph.GetVertexByPoint(ptFrom);
			Vertex vertexByPoint2 = this.graph.GetVertexByPoint(ptTo);
			if (vertexByPoint == null || vertexByPoint2 == null)
			{
				return RouteResult.eInvalidVertex;
			}
			this.RelaxVertexs(vertexByPoint);
			if (GeoUtil.IsEqual(vertexByPoint2.Weight, 1.7976931348623157E+308))
			{
				return RouteResult.eUnconnected;
			}
			this.graph.ResetVertexVisited();
			List<YJKArchMethodLibrary.StructurePoleSuit.Edge> list = new List<YJKArchMethodLibrary.StructurePoleSuit.Edge>();
			if (!this.RoutePath(vertexByPoint2, vertexByPoint, list))
			{
				return RouteResult.eUnconnected;
			}
			lstVertex.Add(vertexByPoint.Point);
			Vertex vertex = vertexByPoint;
			foreach (YJKArchMethodLibrary.StructurePoleSuit.Edge edge in list)
			{
				lstEdge.Add(edge.ElementID);
				long mapVertex = edge.GetMapVertex(vertex.ID);
				Vertex vertexByID = this.graph.GetVertexByID(mapVertex);
				if (vertexByID != null)
				{
					lstVertex.Add(vertexByID.Point);
					vertex = vertexByID;
				}
			}
			return RouteResult.eOK;
		}

		private void RelaxVertexs(Vertex vtxFrom)
		{
			vtxFrom.Weight = 0.0;
			for (Vertex minWeightVertex = this.GetMinWeightVertex(); minWeightVertex != null; minWeightVertex = this.GetMinWeightVertex())
			{
				foreach (long id in minWeightVertex.EdgeList)
				{
					Edge edgeByID = this.graph.GetEdgeByID(id);
					if (edgeByID != null)
					{
						long mapVertex = edgeByID.GetMapVertex(minWeightVertex.ID);
						Vertex vertexByID = this.graph.GetVertexByID(mapVertex);
						if (vertexByID != null)
						{
							double weight = minWeightVertex.Weight + edgeByID.Weight;
							if (minWeightVertex.Weight + edgeByID.Weight < vertexByID.Weight)
							{
								vertexByID.Weight = weight;
							}
						}
					}
				}
				minWeightVertex.Visited = true;
			}
		}

		private Vertex GetMinWeightVertex()
		{
			double num = double.MaxValue;
			Vertex result = null;
			List<Vertex>.Enumerator enumerator = this.graph.VertexEnumerator();
			while (enumerator.MoveNext())
			{
				Vertex vertex = enumerator.Current;
				if (!vertex.Visited && vertex.Weight < num)
				{
					num = vertex.Weight;
					result = vertex;
				}
			}
			return result;
		}

		private bool RoutePath(Vertex vtxFrom, Vertex vtxTo, List<YJKArchMethodLibrary.StructurePoleSuit.Edge> lstEdge)
		{
			if (vtxFrom == null || vtxTo == null)
			{
				return false;
			}
			Vertex vertex = null;
			foreach (long id in vtxFrom.EdgeList)
			{
				Edge edgeByID = this.graph.GetEdgeByID(id);
				if (edgeByID != null)
				{
					long mapVertex = edgeByID.GetMapVertex(vtxFrom.ID);
					Vertex vertexByID = this.graph.GetVertexByID(mapVertex);
					if (vertexByID != null && !GeoUtil.IsEqual(vertexByID.Weight, 1.7976931348623157E+308) && !vertexByID.Visited)
					{
						vertexByID.Visited = true;
						if (vertexByID.Equals(vtxTo))
						{
							lstEdge.Insert(0, edgeByID);
							return true;
						}
						if (GeoUtil.IsEqual(vtxFrom.Weight, vertexByID.Weight + edgeByID.Weight))
						{
							vertex = vertexByID;
							lstEdge.Insert(0, edgeByID);
							break;
						}
					}
				}
			}
			return vertex != null && this.RoutePath(vertex, vtxTo, lstEdge);
		}

		private UndirectGraph graph;
	}
}
